using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

using EntModApp.EntServices;
using EntModApp.EntServices.Data;

namespace DemoApp.DataAccess 
{
  public partial class FavoriteDerivedQueryableRepository : 
    DbContextQueryableRepository<DemoAppModelDbContext, Favorite, FavoriteDerived>,
    IQueryableFavoriteDerivedRepository
  {
    public FavoriteDerivedQueryableRepository(DemoAppModelDbContext context)
      : this(context, false)
    {
    }

    public FavoriteDerivedQueryableRepository()
      : this(new DemoAppModelDbContext(), true)
    {
    }

    protected FavoriteDerivedQueryableRepository(DemoAppModelDbContext context, bool ownContext)
      : base(context, context.Favorites, ownContext)
    {
    }
  }

  public partial class FavoriteDerivedDataRepository : 
    DbContextDataRepository<DemoAppModelDbContext, Favorite, FavoriteDerived, FavoriteDerivedLoadSettings>,
    IFavoriteDerivedRepository
  {
    public FavoriteDerivedDataRepository(DemoAppModelDbContext context)
      : this(context, false)
    {
    }

    public FavoriteDerivedDataRepository()
      : this(new DemoAppModelDbContext(), true)
    {
    }

    protected FavoriteDerivedDataRepository(DemoAppModelDbContext context, bool ownContext)
      : base(context, context.Favorites, ownContext)
    {
    }

    public override IEnumerable<FavoriteDerived> RetrieveAll(out int total, bool retrieveTotal)
    {
      var query = new FavoriteDerivedLoadSettings
      {
        Paging = new PagingSettings { ComputeTotal = retrieveTotal }
      };
      return Retrieve(query, out total);
    }

    public override IEnumerable<FavoriteDerived> Retrieve(FavoriteDerivedLoadSettings query, out int total)
    {
      var dbQuery = DataSet.OfType<FavoriteDerived>().ApplyFavoriteDerivedLoadSettings(query, out total);
      return dbQuery.ToList();
    }

    public override int Count()
    {
      return DataSet.Count();
    }

    public override int Count(FavoriteDerivedLoadSettings query)
    {
      var dbQuery = DataSet.OfType<FavoriteDerived>().ApplyFavoriteDerivedFilterExpression(query.Filter);
      return dbQuery.Count();
    }
  }

  public static class FavoriteDerivedQueryableRepositoryExtensions
  {
    public static IQueryable<FavoriteDerived> ApplyFavoriteDerivedLoadSettings(this IQueryable<FavoriteDerived> query, FavoriteDerivedLoadSettings loadSettings, out int total)
    {
      if (loadSettings == null)
        loadSettings = new FavoriteDerivedLoadSettings();

      query = query.ApplyFavoriteDerivedInclusionSettings(loadSettings.Inclusion);

      query = query.ApplyFavoriteDerivedFilterExpression(loadSettings.Filter);

      query = query.ApplyFavoriteDerivedSortingSettings(loadSettings.Sorting);

      query = query.ApplyPagingSettings(loadSettings.Paging, out total);

      query = query.ApplyFavoriteDerivedSelectionSettings(loadSettings.Selection);

      return query;
    }

    public static IEnumerable<string> GetDirectFavoriteDerivedInclusionPaths(this FavoriteDerivedInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetDirectFavoriteInclusionPaths())
        yield return path;

      if (inclusionSettings.IncludeSubFavorites) yield return "SubFavorites";

      yield break;
    }

    public static IEnumerable<string> GetFavoriteDerivedInclusionPaths(this FavoriteDerivedInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetDirectFavoriteDerivedInclusionPaths())
        yield return path;

      if (inclusionSettings.IncludeSubFavorites)
        foreach (var referencePath in inclusionSettings.IncludedSubFavoritesSettings.Inclusion.GetFavoriteInclusionPaths())
          yield return "SubFavorites." + referencePath;

      yield break;
    }

    public static IQueryable<FavoriteDerived> ApplyFavoriteDerivedInclusionSettings(this IQueryable<FavoriteDerived> query, FavoriteDerivedInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetFavoriteDerivedInclusionPaths())
        query = query.Include(path);

      return query;
    }

    public static IQueryable<FavoriteDerived> ApplyFavoriteDerivedFilterExpression(this IQueryable<FavoriteDerived> query, FavoriteDerivedFilterExpression filterExpression)
    {
      if (filterExpression.ByKey != null)
        query = query.Where(o => o.UserId == filterExpression.ByKey.UserId && o.SubjectId == filterExpression.ByKey.SubjectId);

      var parameter = Expression.Parameter(typeof(FavoriteDerived), "e");
      if (filterExpression.DateOfCreationFilter != null)
        query = query.Where(filterExpression.DateOfCreationFilter.CreateExpressionBody<FavoriteDerived, DateTime?>(FavoriteDerivedPropertyNames.DateOfCreation, parameter).ToLambda<FavoriteDerived>(parameter));
      if (filterExpression.VoteFilter != null)
        query = query.Where(filterExpression.VoteFilter.CreateExpressionBody<FavoriteDerived, double?>(FavoriteDerivedPropertyNames.Vote, parameter).ToLambda<FavoriteDerived>(parameter));

      if (filterExpression.Skip.HasValue)
        query = query.Skip(filterExpression.Skip.Value);
      if (filterExpression.Take.HasValue)
        query = query.Take(filterExpression.Take.Value);

      return query;
    }

    public static IQueryable<FavoriteDerived> ApplyFavoriteDerivedSortingSettings(this IQueryable<FavoriteDerived> query, FavoriteDerivedSortingSettings sortingSettings)
    {
      return query;
    }

    public static IQueryable<FavoriteDerived> ApplyFavoriteDerivedSelectionSettings(this IQueryable<FavoriteDerived> query, FavoriteDerivedSelectionSettings selectionSettings)
    {
      return query;
    }

  }
}
